///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////
// PictureCtrl.cpp
// 
// Author: Tobias Eiseler
//
// E-Mail: tobias.eiseler@sisternicky.com
// 
// Function: A MFC Picture Control to display
//           an image on a Dialog, etc.
///////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "ArchiveProcGUI.h"
#include "PictureCtrl.h"
#include "PtShapeType.h"


#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;


//Macro to release COM Components

#ifdef SAFE_RELEASE
#undef SAFE_RELEASE
#endif
#define SAFE_RELEASE(x) do{\
							if((x) != NULL)\
							{\
								while((x)->Release() != 0);\
								(x) = NULL;\
							}\
						}while(0)

//BEGIN_MESSAGE_MAP(CPictureCtrl, CStatic)
//ON_WM_ERASEBKGND()
//ON_WM_PAINT()
//END_MESSAGE_MAP()


CPictureCtrl::CPictureCtrl(void)
	:CStatic()
	, m_pStream(NULL)
	, m_bIsPicLoaded(FALSE)
{

}


CPictureCtrl::~CPictureCtrl(void)
{
	//Tidy up
	FreeData();

}

BOOL CPictureCtrl::LoadFromStream(IStream *piStream)
{
	//Set success error state
	SetLastError(ERROR_SUCCESS);

	FreeData();

	//Check for validity of argument
	if(piStream == NULL)
	{
		SetLastError(ERROR_INVALID_ADDRESS);
		return FALSE;
	}

	//Allocate stream
	DWORD dwResult = CreateStreamOnHGlobal(NULL, TRUE, &m_pStream);
	if(dwResult != S_OK)
	{
		SetLastError(dwResult);
		return FALSE;
	}

	//Rewind the argument stream
	LARGE_INTEGER lInt;
	lInt.QuadPart = 0;
	piStream->Seek(lInt, STREAM_SEEK_SET, NULL);

	//Read the lenght of the argument stream
	STATSTG statSTG;
	dwResult = piStream->Stat(&statSTG, STATFLAG_DEFAULT);
	if(dwResult != S_OK)
	{
		SetLastError(dwResult);
		SAFE_RELEASE(m_pStream);
		return FALSE;
	}

	//Copy the argument stream to the class stream
	piStream->CopyTo(m_pStream, statSTG.cbSize, NULL, NULL);

	//Mark as loaded
	m_bIsPicLoaded = TRUE;

	Invalidate();
	RedrawWindow();

	return TRUE;
}

BOOL CPictureCtrl::LoadFromStream(BYTE* pData, size_t nSize)
{
	//Set success error state
	SetLastError(ERROR_SUCCESS);
	FreeData();

	//Allocate stream
	DWORD dwResult = CreateStreamOnHGlobal(NULL, TRUE, &m_pStream);
	if(dwResult != S_OK)
	{
		SetLastError(dwResult);
		return FALSE;
	}

	//Copy argument data to the stream
	dwResult = m_pStream->Write(pData, (ULONG)nSize, NULL);
	if(dwResult != S_OK)
	{
		SetLastError(dwResult);
		SAFE_RELEASE(m_pStream);
		return FALSE;
	}

	//Mark as loaded
	m_bIsPicLoaded = TRUE;

	Invalidate();
	RedrawWindow();

	return TRUE;
}

BOOL CPictureCtrl::LoadFromFile(CString &szFilePath)
{
	
	//Set success error state
	SetLastError(ERROR_SUCCESS);
	FreeData();

	//Allocate stream
	DWORD dwResult = CreateStreamOnHGlobal(NULL, TRUE, &m_pStream);
	if(dwResult != S_OK)
	{
		SetLastError(dwResult);
		return FALSE;
	}

	//Open the specified file
	CFile cFile;
	CFileException cFileException;
	if(!cFile.Open(szFilePath, CStdioFile::modeRead | CStdioFile::typeBinary, &cFileException))
	{
		SetLastError(cFileException.m_lOsError);
		SAFE_RELEASE(m_pStream);
		return FALSE;
	}

	//Copy the specified file's content to the stream
	BYTE pBuffer[1024] = {0};
	while(UINT dwRead = cFile.Read(pBuffer, 1024))
	{
		dwResult = m_pStream->Write(pBuffer, dwRead, NULL);
		if(dwResult != S_OK)
		{
			SetLastError(dwResult);
			SAFE_RELEASE(m_pStream);
			cFile.Close();
			return FALSE;
		}
	}

	//Close the file
	cFile.Close();

	//Mark as Loaded
	m_bIsPicLoaded = TRUE;

	RECT rc;
	GetClientRect(&rc);

	Image image(m_pStream);
	RectF boundsRect;
	Unit unit;
	image.GetBounds(&boundsRect, &unit);
	SetWindowPos(&CWnd::wndTop, rc.left,  rc.top, (INT)boundsRect.Width, (INT)boundsRect.Height, SWP_NOMOVE );

	Invalidate();
	RedrawWindow();

	return TRUE;
}


//Overload - Single load function
BOOL CPictureCtrl::Load(CString &szFilePath)
{
	return false; //LoadFromFile(szFilePath);
}

BOOL CPictureCtrl::Load(IStream* piStream)
{
	return LoadFromStream(piStream);
}

BOOL CPictureCtrl::Load(BYTE* pData, size_t nSize)
{
	return LoadFromStream(pData, nSize);
}


void CPictureCtrl::FreeData()
{
	m_bIsPicLoaded = FALSE;
	SAFE_RELEASE(m_pStream);
}

void CPictureCtrl::PreSubclassWindow()
{
	CStatic::PreSubclassWindow();
	ModifyStyle(0, SS_OWNERDRAW);
}

void CPictureCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	//Check if pic data is loaded
	if(m_bIsPicLoaded)
	{
		Graphics graphics(lpDrawItemStruct->hDC);
		Image image(m_pStream);
		RectF boundsRect;
		Unit unit;
		image.GetBounds(&boundsRect, &unit);

		Matrix translationMatrix(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);

		int brightness = 0;
		int contrast = 0;
		ImageProps *pSelImageProps = '\0';
		if (pSelImageProps = GetSelImageProperties())
		{
			contrast = pSelImageProps->GetContrastValue();
			brightness = pSelImageProps->GetBrightnessValue();
		}

		BrightnessContrastParams briConParams;
		briConParams.brightnessLevel = brightness;
		briConParams.contrastLevel = contrast;
		
		BrightnessContrast briCon;
		briCon.SetParameters(&briConParams);
		graphics.DrawImage(&image, &boundsRect, &translationMatrix, &briCon, NULL, unit);

		DrawImageObjects(&graphics, pSelImageProps);


	}
}

BOOL CPictureCtrl::OnEraseBkgnd(CDC *pDC)
{
	if(m_bIsPicLoaded)
	{

		//Get control measures
		RECT rc;
		this->GetClientRect(&rc);

		Graphics graphics(pDC->GetSafeHdc());
		LARGE_INTEGER liSeekPos;
		liSeekPos.QuadPart = 0;
		m_pStream->Seek(liSeekPos, STREAM_SEEK_SET, NULL);
		Image image(m_pStream);
		graphics.DrawImage(&image, (INT)rc.left, (INT)rc.top, (INT)(rc.right-rc.left), (INT)(rc.bottom-rc.top));

		DrawImageObjects(&graphics,  GetSelImageProperties());

		return TRUE;
	}
	else
	{
		return CStatic::OnEraseBkgnd(pDC);
	}
}

void	CPictureCtrl::DrawImageObjects(void *pGraphics, ImageProps *pSelImageProps)
{
	if ( IsShowImageObjects() && ( pSelImageProps != '\0') && (pGraphics != '\0'))
	{
		ImageObject *pImageObject = '\0';
		int cnt = pSelImageProps->GetImageObjectCnt();
		for (int x = 0; x < cnt; x++ )
		{
			if ((pImageObject = pSelImageProps->GetImageObject(x)) != '\0')
			{
				ImageObjectTypeTool *pTool = GetToolForImageObject(pImageObject);
				if (pTool != '\0')
				{
					if (pImageObject == pSelImageProps->GetSelImageObject())
						pTool->DrawImageObject(pGraphics, pImageObject, true);
					else
						pTool->DrawImageObject(pGraphics, pImageObject, false);
				}
			}
		}
	}
}

BEGIN_MESSAGE_MAP(CPictureCtrl, CStatic)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()


void CPictureCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
	ImageObjectTypeTool *pActiveTool = GetActiveTool();
	if (pActiveTool != '\0')
	{
		ImagePt pt(point.x, point.y);

		ImageProps *pSelImageProps = GetSelImageProperties();
		if (pSelImageProps == '\0')
			CArchiveProcGUIApp::UserErrorMsg("internal error", "no properties set for this image", "success", "select a different image and then retry");
		else
		{
			if (pSelImageProps->CanCreateExpertImageObject(pActiveTool->GetImageObjectType()) == false)
				CArchiveProcGUIApp::UserErrorMsg("user error", "cannot create another image object of selected type for this image", "success", " delete the selected image object (Edit | Erase) and then try again");
			else
			{
				//save existing properties of selected image object, if any before creating new image object and setting its (default) properties
				CArchiveProcGUIDoc::GetActiveDoc()->DoUpdateAllViews(GetParent(),CArchiveProcGUIDoc::UpdateAllViewsSave, NULL); 

				ImageObject *pNewImageObj = '\0';
				if ( (pNewImageObj = pActiveTool->DoLButtonDown(nFlags, pt, GetSelImageProperties())) == '\0')  //creates ImageObject and calls SetSelImageObject() to save existing image object, load new image object, call UpdateAllViews(...)
					CArchiveProcGUIApp::UserErrorMsg("user error", "failed to select image object", "success", "create an image object and try again"); 
				else
				{
					ASSERT(pNewImageObj == GetSelImageObject());
					RefreshImageObjectArea(GetSelImageObject(), CArchiveProcGUIDoc::UpdateAllViewsCreateImageObject);
				}
			}
		}
	}
	CStatic::OnLButtonDown(nFlags, point);
}


void CPictureCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
	ImageObjectTypeTool *pActiveTool = GetActiveTool();
	if (pActiveTool != '\0')
	{
		ImagePt pt(point.x, point.y);
		if ( pActiveTool->DoLButtonUp(nFlags, pt, GetSelImageObject()) )	//close existing image object to complete its creation
		{
			RefreshImageObjectArea(GetSelImageObject(), CArchiveProcGUIDoc::UpdateAllViewsCompleteImageObject);
		}
	}
	CStatic::OnLButtonUp(nFlags, point);
}


void CPictureCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
	if ( nFlags & MK_LBUTTON )
	{
		ImageObjectTypeTool *pActiveTool = GetActiveTool();
		if (pActiveTool != '\0')
		{
			ImagePt pt(point.x, point.y);
			if ( pActiveTool->DoMouseMove(nFlags, pt, GetSelImageObject()) )	//update existing image object 
			{
				RefreshImageObjectArea(GetSelImageObject(), CArchiveProcGUIDoc::UpdateAllViewsUpdateImageObject);
			}
		}
	}
	CStatic::OnMouseMove(nFlags, point);
}

void CPictureCtrl::RefreshImageObjectArea(ImageObject *pSelImageObject, int UpdateAllViewslHint)
{
	if (pSelImageObject != '\0')
	{
		if (UpdateAllViewslHint != CArchiveProcGUIDoc::UpdateAllViewsCreateImageObject)
		{
			ImageRect imgRect(pSelImageObject->GetContainingRect());

			CRect rect(imgRect.GetTopLeft().x, imgRect.GetTopLeft().y, imgRect.GetBotRight().x, imgRect.GetBotRight().y);
			rect.NormalizeRect();
			InvalidateRect(&rect, 0);
			RedrawWindow();
		}
		CArchiveProcGUIDoc::GetActiveDoc()->DoUpdateAllViews(GetParent(),UpdateAllViewslHint, NULL); //update alll views except ImageForm
	}
}




